package com.suxin.timer;

import lombok.Getter;
import lombok.SneakyThrows;
import org.junit.Test;

import java.io.IOException;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @author Tang
 * @classname TimerTest
 * @description [  ]
 * @date 2022/2/3 22:18
 */
public class TimerTest {

    public static void main(String[] args) {
        Timer timer = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("TimerTask...run...");
            }
        };
        timer.schedule(timerTask, 5000);
        try {
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @Test
    public void test01() {
        Task task = new Task();
        task.schedule(5,() -> {
            System.out.println("test01...");
        });
        try {
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static class Task {

        private TaskQueue taskQueue = new TaskQueue();

        private Thread thread = null;

        public Task() {
            thread = new Thread(new TaskThread(taskQueue), "TaskThread");
            thread.start();
        }

        public void schedule(int second, Runnable runnable) {
            taskQueue.put(new TaskInfo(second, runnable));
        }

    }

    static class TaskThread implements Runnable {

        private TaskQueue taskQueue;

        public TaskThread(TaskQueue taskQueue) {
            this.taskQueue = taskQueue;
        }

        @SneakyThrows
        @Override
        public void run() {
            mainLoop();
        }

        private void mainLoop() throws InterruptedException {
            while (true) {
                TaskInfo taskInfo = taskQueue.getMin();
                if (taskInfo == null) {
                    taskQueue.wait();
                    continue;
                }
                long currentTimeMillis = System.currentTimeMillis();
                boolean taskFired;
                if (taskFired = (currentTimeMillis <= taskInfo.getTriggerTime())) {
                    taskQueue.removeMin();
                } else {
                    taskQueue.wait(taskInfo.getTriggerTime() - currentTimeMillis);
                }
                if (!taskFired) {
                    taskInfo.run();
                }
            }
        }
    }

    static class TaskQueue {

        private TaskInfo[] taskInfos = new TaskInfo[128];

        private int size = 0;

        public TaskQueue() {
        }

        public void put(TaskInfo taskInfo) {
            if (size + 2 == taskInfos.length) {
                taskInfos = Arrays.copyOf(taskInfos, 2 * taskInfos.length);
            }
            taskInfos[size++] = taskInfo;
            this.notify();
        }

        public TaskInfo getMin() {
            return taskInfos[0];
        }

        public void removeMin() {
            taskInfos[0] = taskInfos[size];
            taskInfos[size--] = null;
        }
    }


    static class TaskInfo {

        private int second;

        @Getter
        private long triggerTime;

        private Runnable runnable;

        public TaskInfo(int second, Runnable runnable) {
            this.second = second;
            this.triggerTime = System.currentTimeMillis() + (second * 1000);
            this.runnable = runnable;
        }

        public void run() {
            this.runnable.run();
        }

    }

}